<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>面向对象 拖拽</title>

  <style>
    #drag1 {
      width: 100px;
      height: 100px;
      background-color: orange;
      box-sizing: border-box;
    }
    #drag2 {
      width: 100px;
      height: 100px;
      background-color: aquamarine;
      box-sizing: border-box;
    }

    #drag3 {
      width: 100px;
      height: 100px;
      background-color: orchid;
      box-sizing: border-box;
    }

    #drag4 {
      width: 100px;
      height: 100px;
      background-color: chartreuse;
      box-sizing: border-box;
    }
  </style>
</head>
<body>
  <div id="drag1"></div>
  <div id="drag2"></div>
  <div id="drag3"></div>
  <div id="drag4"></div>
</body>

<script>

// 对象
// 实例
// 类
class Element {

}


class DragElement {
  constructor(selector) {
    this.element = typeof selector === 'Object' ? selector : document.getElementById(selector)
    this.startX = 0
    this.startY = 0
    this.sourceX = 0
    this.sourceY = 0
    this.transform = this.getTransform()
    this.setDrag()
    // this.collision = false
    // 我与别的方块是否有交集
    this.collision = new Map()
    // 当前有多少个元素跟我有交集
    this.marks = 0
    return this
  }

  setCollision(element, value) {
    this.collision.set(element, value)
  }
  getCollision(element) {
    this.collision.get(element)
  }

  setDrag() {
    var self = this;
    this.element.addEventListener('mousedown', start, false);
    function start(event) {
      self.startX = event.pageX;
      self.startY = event.pageY;

      var pos = self.getPosition();

      self.sourceX = pos.x;
      self.sourceY = pos.y;

      document.addEventListener('mousemove', move, false);
      document.addEventListener('mouseup', end, false);
    }

    function move(event) {
      var currentX = event.pageX;
      var currentY = event.pageY;

      var distanceX = currentX - self.startX;
      var distanceY = currentY - self.startY;

      var curPos = {
        x: (self.sourceX + distanceX).toFixed(),
        y: (self.sourceY + distanceY).toFixed()
      }

      self.setPostion(curPos)
      self.onDrag && self.onDrag(curPos)
    }

    function end(event) {
      document.removeEventListener('mousemove', move);
      document.removeEventListener('mouseup', end);
      // do other things
    }
  }

  // 拖拽过程中，传入位置信息
  onDrag(postion) {
    const _thisInfo = this.getBoundingClientRect()

    var onMap = new Map()
    var offMap = new Map()

    this.queue.forEach((element, i) => {
      const _targInfo = element.getBoundingClientRect()

      const collisionX =
        _thisInfo.x > _targInfo.x - _thisInfo.width && 
        _thisInfo.x < _targInfo.x + _targInfo.width
      
      const collisionY =
        _thisInfo.y > _targInfo.y - _thisInfo.height &&
        _thisInfo.y < _targInfo.y + _targInfo.height

      if (collisionX && collisionY) {
        if (!this.collision.get(element)) {
          // this.collision.set(element, true)
          this.setCollision(element, true)
          element.setCollision(this, true)
          onMap.set(element, i)
        }
      } else {
        if (this.collision.get(element)) {
          this.setCollision(element, false)
          element.setCollision(this, false)
          offMap.set(element, i)
        }
      }
    })

    Array.from(onMap).forEach((item) => {
      this.onCollisionDections[item[1]](item[0])
    })

    onMap.clear()

    Array.from(offMap).forEach((item) => {
      this.offCollisionDections[item[1]](item[0])
    })
    offMap.clear()
  }

  getAttribute(property) {
    var getStyle = document.defaultView.getComputedStyle
    return getStyle ? getStyle(this.element, false)[property] : this.element.currentStyle[property]
  }

  getPosition() {
    var pos = { x: 0, y: 0 }
    if (this.transform) {
      var transformValue = this.getAttribute(this.transform)
      if (transformValue === 'none') {
        this.element.style[this.transform] = 'translate(0, 0)'
      } else {
        var temp = transformValue.match(/-?\d+/g)
        pos = {
          x: parseInt(temp[4].trim()),
          y: parseInt(temp[5].trim())
        }
      }
    } else {
      var position = this.getAttribute('position')
      if (position === 'static') {
        this.element.style.position = 'relative'
      } else {
        pos = {
          x: parseInt(this.getAttribute('left') ? this.getAttribute('left') : 0),
          y: parseInt(this.getAttribute('top') ? this.getAttribute('top') : 0)
        }
      }
    }
    return pos
  }

  setPostion(pos) {
    if (this.transform) {
      this.element.style[this.transform] = 'translate(' + pos.x + 'px, ' + pos.y + 'px)';
    } else {
      this.element.style.left = pos.x + 'px';
      this.element.style.top = pos.y + 'px';
    }
  }

  // 私有方法，用来获取 transform 的兼容性写法
  getTransform() {
    var transform = '', divStyle = document.createElement('div').style,
      transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'], i = 0,
      length = transformArr.length;
    for(; i < length; i++) {
      if (transformArr[i] in divStyle) {
        return transform = transformArr[i]
      }
    }

    return transform
  }

  mark() {
    this.marks += 1
    if (this.marks == 1) {
      this.element.style.border = '2px solid red'
    }
  }
  cleanMark() {
    this.marks -= 1
    if (this.marks === 0) {
      this.element.style.border = '0px solid red'
    }
  }

  getBoundingClientRect() {
    return this.element.getBoundingClientRect()
  }

  collisionDetectionCancel() {}

  queue = []
  onCollisionDections = []
  offCollisionDections = []
  
  collisionDetectionSubscribe(element, onCollisionDection, offCollisionDection) {
    var _on = onCollisionDection
    if (!_on) {
      _on = (element) => {
        element.mark()
        this.mark()
      }
    }
    var _off = offCollisionDection
    if (!_off) {
      _off = (element) => {
        element.cleanMark()
        this.cleanMark()
      }
    }

    if (!this.queue.includes(element)) {
      this.queue.push(element)
      this.onCollisionDections.push(_on)
      this.offCollisionDections.push(_off)
      element.collisionDetectionSubscribe(this)
    }
  }
}

let drag1 = new DragElement('drag1')
let drag2 = new DragElement('drag2')
let drag3 = new DragElement('drag3')
let drag4 = new DragElement('drag4')

// 绑定
drag1.collisionDetectionSubscribe(drag2)
drag1.collisionDetectionSubscribe(drag3)
drag1.collisionDetectionSubscribe(drag4)

drag2.collisionDetectionSubscribe(drag4)
drag2.collisionDetectionSubscribe(drag1)

let a = {
  b: {c: 1}
}


</script>
</html>